#!/usr/bin/env python
"""
a module to detect dns64 and translate ipv4 literals into their NAT64 ipv6 address
"""

import socket
import struct

__all__ = ['nat64']

prefix = None

def convert_to_96(ipv6):
  """
  convert an ipv6 into a /96 subnet
  """
  ipv6 = socket.inet_pton(socket.AF_INET6, ipv6)
  bytes = list(struct.unpack("!4L",ipv6))
  bytes[3] = 0;
  ipv6 = struct.pack("!4L",bytes[0],bytes[1],bytes[2],bytes[3])
  return socket.inet_ntop(socket.AF_INET6, ipv6)

def init():
  """
  detect DNS64 by doing AAAA lookups on the .com nameservers.  If more than 1
  nameserver is in the same /96, we're dealing with DNS64.
  """
  global prefix
  v6_prefixes = {}

  for rootname in "abcdefghijklm":
    try:
      addrlist = socket.getaddrinfo(rootname+'.gtld-servers.net', None, socket.AF_INET6, socket.SOCK_DGRAM)
    except socket.gaierror:
      addrlist = [] # no ipv6 for that rootname
    for res in addrlist:
      subnet = convert_to_96(res[4][0])
      if(subnet not in v6_prefixes):
        v6_prefixes[subnet] = 0
      v6_prefixes[subnet] += 1

  if len(v6_prefixes) > 0:
    highestprefix = max(v6_prefixes, key=v6_prefixes.get)
    maxcount = v6_prefixes[highestprefix]
    if(maxcount > 1):
      prefix = highestprefix
      return

  prefix = None

def nat64(ipv4):
  """
  argument: the ipv4 literal
  returns: a tuple of the protocol to use (AF_INET/AF_INET6) and the ip to connect to (as a string)
  """
  global prefix

  if(prefix == None):
    return (socket.AF_INET, ipv4)

  tmp_prefix = socket.inet_pton(socket.AF_INET6, prefix)
  ipv6_bytes = list(struct.unpack("!4L",tmp_prefix))

  ipv4 = socket.inet_pton(socket.AF_INET, ipv4)
  ipv4_bytes = list(struct.unpack("!L",ipv4))

  ipv6_bytes[3] = ipv4_bytes[0]
  ipv6 = struct.pack("!4L",ipv6_bytes[0],ipv6_bytes[1],ipv6_bytes[2],ipv6_bytes[3])

  return (socket.AF_INET6, socket.inet_ntop(socket.AF_INET6, ipv6))

def main():
  print "127.0.0.1",nat64("127.0.0.1")
  print "1.2.3.4",nat64("1.2.3.4")

init()
if __name__ == '__main__':
  main()
